package com.macro.mall.portal.wxpay.helper;


import com.macro.mall.portal.util.MyCodeUtil;
import com.macro.mall.portal.util.MyHttpUtil;
import com.macro.mall.portal.wxpay.enums.TradeTypeEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import net.logstash.logback.encoder.org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.Map.Entry;

@Slf4j
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class MyWeChatPayUtil {


    /**
     * 统一下单
     */
    private final String unifiedorder = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    /**
     * 查询订单
     */
    private final String orderquery = "https://api.mch.weixin.qq.com/pay/orderquery";

    /**
     * 申请退款
     */
    private final String refund = "https://api.mch.weixin.qq.com/secapi/pay/refund";

    /**
     * APP_ID 融资小灵通
     */
    private final static String APP_ID = "wx9e30c3907000204c";
    /**
     * mchId 参数
     */
    private final static String MCH_ID = "1608763457";


    /**
     * mchKey
     */
    private final static String MCH_KEY = "4OxipTuLE1wc2V0JTSMr8Fb2jt3sek6t";


    public Document unifiedOrder(String attach, String ip, String out_trade_no, String body, Integer total_fee, TradeTypeEnum tradeTypeEnum, String notifyUrl, String openid) throws Exception {
        String appid = APP_ID;
//        String mchId = weChantConfigMapper.selectById("mchId").getValue();
        String mchId = MCH_ID;
//        String notify_url = weChantConfigMapper.selectById("notify_url").getValue();
        String notify_url = notifyUrl;

        Document wxPayXml = DocumentHelper.createDocument();
        Element rootElement = wxPayXml.addElement("xml");

        rootElement.addElement("appid").setText(appid);
        rootElement.addElement("mch_id").setText(mchId);
        String nonce_str = MyCodeUtil.getRandomStr(10, 32);
        rootElement.addElement("nonce_str").setText(nonce_str);// 随机字符串，长度要求在32位以内。
        rootElement.addElement("body").setText(body);// 商品简单描述
        rootElement.addElement("openid").setText(openid);// 商品简单描述

        if (StringUtils.isNotBlank(attach)) {
            rootElement.addElement("attach").addCDATA(attach);// 附加数据，在查询API和支付通知中原样返回，可作为自定义参数使用。
        }
        rootElement.addElement("out_trade_no").setText(out_trade_no);// 商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*且在同一个商户号下唯一。
        rootElement.addElement("total_fee").setText(total_fee.toString());// 支付金额，单位：分
//        rootElement.addElement("total").setText(total_fee.toString());// 支付金额，单位：分
        rootElement.addElement("spbill_create_ip").setText(ip);// 支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP


        rootElement.addElement("notify_url").setText(notify_url);// 异步接收微信支付结果通知的回调地址。
        rootElement.addElement("trade_type").setText(tradeTypeEnum.getValue());// 交易类型
//        if (sceneInfo != null) {
//            rootElement.addElement("scene_info").setText(sceneInfo.toString());// 场景信息
//        }
        String sign = getSign(wxPayXml);
        rootElement.addElement("sign").setText(sign);// 通过签名算法计算得出的签名值（需最后添加该参数）

//        log.info("微信支付-统一下单请求body：{}", wxPayXml.getRootElement().asXML());

        Document resXml = MyHttpUtil.sendXmlPOST(unifiedorder, wxPayXml);

//        log.info("微信支付-统一下单响应body：{}", resXml.getRootElement().asXML());

        return resXml;
    }

    /**
     * 查询订单
     *
     * @param orderNumber 订单号
     * @return
     * @throws Exception
     */
    public Document orderQuery(String orderNumber) throws Exception {
        String appid = APP_ID;
        String mchId = MCH_ID;

        Document reqXml = DocumentHelper.createDocument();
        Element reqRoot = reqXml.addElement("xml");

        reqRoot.addElement("appid").setText(appid);
        reqRoot.addElement("mch_id").setText(mchId);// 商户号
//        reqRoot.addElement("transaction_id").setText(orderId);// 微信订单号
        reqRoot.addElement("out_trade_no").setText(orderNumber);// 商户订单号
        reqRoot.addElement("nonce_str").setText(MyCodeUtil.getRandomStr(10, 32));// 随机字符串
        String sign = getSign(reqXml);
        reqRoot.addElement("sign").setText(sign);// 签名

        Document resXml = MyHttpUtil.sendXmlPOST(orderquery, reqXml);

        return resXml;
    }


    /**
     * 申请退款
     *
     * @param orderId  订单号
     * @param refundId 商户退款单号
     * @param totalFee 退款金额即订单金额
     * @param reason   退款原因，可为null
     * @return
     * @throws Exception
     */
    public Document refund(String orderId, String refundId, Integer totalFee, String reason) throws Exception {
        String appid = APP_ID;
        String mchId = MCH_ID;
        String notify_url = "notify_URL";

        Document reqXml = DocumentHelper.createDocument();
        Element reqRoot = reqXml.addElement("xml");

        reqRoot.addElement("appid").setText(appid);
        reqRoot.addElement("mch_id").setText(mchId);
        reqRoot.addElement("nonce_str").setText(MyCodeUtil.getRandomStr(10, 32));
//        reqRoot.addElement("transaction_id").setText(transaction_id);// 微信订单号
        reqRoot.addElement("out_trade_no").setText(orderId);// 商户订单号
        reqRoot.addElement("out_refund_no").setText(refundId);// 商户退款单号
        reqRoot.addElement("total_fee").setText(totalFee.toString());// 订单金额
        reqRoot.addElement("refund_fee").setText(totalFee.toString());// 退款金额
        if (StringUtils.isNotEmpty(reason)) {
            reqRoot.addElement("refund_desc").setText(reason);// 退款原因
        }
        reqRoot.addElement("notify_url").setText(notify_url);// 退款通知回调地址
        reqRoot.addElement("sign").setText(getSign(reqXml));// 签名

        Document resXml = MyHttpUtil.sendXmlPOST(refund, reqXml);

        return resXml;
    }

    /**
     * xml格式参数
     *
     * @param sourceXml
     * @return
     */
    public String getSign(Document sourceXml) {
        Map<String, String> map = new HashMap<String, String>();

        Iterator<?> iterator = sourceXml.getRootElement().elementIterator();
        while (iterator.hasNext()) {
            Element attribute = (Element) iterator.next();
            String name = attribute.getName();
            String text = attribute.getText();
            if (StringUtils.isNotEmpty(text)) {
                map.put(name, text);
            }
        }

        return getSign(map);
    }

    /**
     * json格式参数
     *
     * @param sourceJson
     * @return
     */
    public String getSign(JSONObject sourceJson) {
        Map<String, String> map = new HashMap<>();

        Set<String> keys = sourceJson.keySet();
        for (String key : keys) {
            String value = sourceJson.optString(key);
            map.put(key, value);
        }

        return getSign(map);
    }

    /**
     * 根据微信签名算法，获取参数签名sign值
     *
     * @return 生成签名的参数
     */
    public String getSign(Map sourceMap) {
        List<Entry<String, String>> infoIds = new ArrayList<Entry<String, String>>(sourceMap.entrySet());
        // 对所有传入参数按照字段名的 ASCII 码从小到大排序（字典序）
        Collections.sort(infoIds, new Comparator<Entry<String, String>>() {
            public int compare(Entry<String, String> o1, Entry<String, String> o2) {
                return ((String) o1.getKey()).compareToIgnoreCase((String) o2.getKey());
            }
        });


        StringBuffer result = new StringBuffer();

        for (Entry<String, String> entry : infoIds) {
            String key = entry.getKey();
            String value = entry.getValue().toString();
            result.append(key + "=" + value + "&");
        }

        String key = MCH_KEY;
        result.append("key=" + key);

        String sign = MyCodeUtil.MD5Encryption(result.toString()).toUpperCase();

        return sign;
    }

    /**
     * 签名验证
     *
     * @param sourceXml
     * @return
     */
    public boolean checkSign(Document sourceXml) {
        boolean checked = false;

        try {
            Element reqRoot = sourceXml.getRootElement();

            String sign = reqRoot.element("sign").getText();
            reqRoot.remove(reqRoot.element("sign"));

            String localSign = getSign(sourceXml);

            checked = localSign.equals(sign);

        } catch (Exception e) {

        }

        return checked;
    }

}
